<html><head><title>解读Tapestry5.1——页面渲染(续2)</title></head><body><div id='tit'>解读Tapestry5.1——页面渲染(续2)</div><div id='cate'>源码品读</div><div id='date'>2010年08月25日 星期三 11:00 A.M.</div><div id='page'>10</div><a id='url' href='http://hi.baidu.com/hxzon/blog/item/6a2181d666e6902106088b36.html'>http://hi.baidu.com/hxzon/blog/item/6a2181d666e6902106088b36.html</a><div id='cnt'><h1>解读Tapestry5.1——页面渲染(续2)</h1> 
<p><a href="http://blog.csdn.net/mindhawk/archive/2009/12/16/5021371.aspx">http://blog.csdn.net/mindhawk/archive/2009/12/16/5021371.aspx</a></p> 
<p>tapestry源码品读</p> 
<p><strong>4.2调度策略</strong></p> 
<p>上一节提到了一个渲染队列org.apache.tapestry5.internal.services.PageRenderQueue。这个队列是Tapestry调度页面渲染的核心，它负责激发组件的不同生命周期，调度整棵组件树依次执行，生成整个HTML页面。</p> 
<p>在介绍具体的调度策略之前，我先介绍一些Tapestry组织组件的结构，这样才能更好的理解调度策略。</p> 
<p>Tapestry组件在运行时有两个主要结构，一个是org.apache.tapestry5.ComponentResources，一个是 org.apache.tapestry5.internal.structure.ComponentPageElement。</p> 
<p>ComponentResources是用于管理组件资源的结构，通常可以看作组件实例。它维护的是组件的资源，以及组件容器以及它的子组件的关系。比如页面组件是谁，直接使用它的组件是谁，但它并不包含组件在模板中的关系，也就是模板中组件之间的嵌套关系。</p> 
<p>ComponentPageElement则是用于维护组件在模板中的嵌套关系的结构，主要在渲染过程中使用。</p> 
<p>我们用下面的一个页面说明这两个结构的区别。</p> 
<p>Example.tml</p> 
<pre>&lt;html xmlns:t=&quot;http://tapestry.apache.org/schema/tapestry_5_1_0.xsd&quot;&gt;<br />&lt;body&gt;<br /> &lt;div t:type=&quot;div&quot;&gt;<br />  &lt;span t:type=&quot;WriteValue&quot; value=&quot;value&quot;/&gt;<br /> &lt;/div&gt;<br />    &lt;form t:type=&quot;form&quot;&gt;<br />     &lt;input t:type=&quot;textfield&quot; value=&quot;value&quot;/&gt;<br />     &lt;input t:type=&quot;submit&quot; value=&quot;submit&quot;/&gt;<br />    &lt;/form&gt;<br />&lt;/body&gt;<br />&lt;/html&gt;</pre> 
<p>Example.java</p> 
<pre>public class Example {<br /> @Property<br /> @Persist<br /> private String value;<br />}</pre> 
<p>为了说明问题，我特地写了两个组件，一个是div（输出一个div标签），一个是WriteValue（输出参数value的值）。它们的代码分别如下：</p> 
<p>Div.java (该组件没有模板)</p> 
<pre>public class Div {<br /> void beginRender(MarkupWriter writer) {<br />  writer.element(&quot;div&quot;);<br /> }<br /> void afterRender(MarkupWriter writer) {<br />  writer.end(); <br /> }<br />}</pre> 
<p>WriteValue.tml</p> 
<pre>&lt;span xmlns:t=&quot;http://tapestry.apache.org/schema/tapestry_5_1_0.xsd&quot;&gt;<br />    ${value}<br />&lt;/span&gt;</pre> 
<p>WriteValue.java</p> 
<pre>public class WriteValue {<br /> @Property<br /> @Parameter(defaultPrefix=BindingConstants.PROP)<br /> private String value;<br />}</pre> 
<p>WriteValue和Div组件是两个非常简单的组件，它们并没有实际存在的价值，目的只是为了说明组件的渲染过程。</p> 
<p>Example页面对应的ComponentPageElement组织组件的架构如下图所示。</p> 
<p> <img src="image/解读Tapestry5.1——页面渲染(.47fe92eeaebb4a6d2cf534f7.jpg" /><p class="origImg">http://hiphotos.baidu.com/hxzon/pic/item/47fe92eeaebb4a6d2cf534f7.jpg</p></p> 
<p>图中绿色代表一个页面，也就是我们例子中的Example页面。黑色的标识一个HTML的标签，这些标签并不是Tapestry的组件，但是框架会 将它们按照标签处理，为什么这么处理我们在后续的中间结构说明。蓝色表示组件，页面中总共用到了Div、Form、WriteValue、 TextField和Submit组件。黄色表示与组件关联的模板或者内嵌元素(body)。红色表示表达式，用于输出value的表达式。</p> 
<p>ComponentResources又是如何组织的呢?</p> 
<p>这里就不给出图形说明了，因为它的结构更简单直接。Div、Form、WriteValue、TextField和Submit组件的父节点都是Example页面。</p> 
<p>之所以要用两个结构是因为，一个是用于显示的结构，一个是用于操作的结构。想想如果没有树形的结构，就很难输出与模板对应的HTML页面。而当操作的时候，需要的是数据绑定和事件通知。而此时，我们只需要知道一个页面使用了那些组件，并不关心它们之间的排列结构。</p> 
<p>理解了ComponentPageElement的组件树，再回想一下组件的生命周期，就能基本上了解渲染的调度过程了。</p> 
<p>对于非组件的结构，比如Start[HTML]，Expression等直接输出就可以了，而对于组件，则递归的调度其执行生命周期。这样，最后就能完整的输出整个页面了。</p> 
<p>整个渲染的流程大致如下：</p> 
<p>Example页面首先被激活，由于没有定义生命周期响应函数，直接进入模板渲染阶段。此时，Start[HTML]和Start[Body]这连 个开始Tag被执行。之后，进入Div组件，由于Div组件具有BeginRender函数，所以进入该函数输出Div的开始Tag。接着因为Div组件 没有模板进入渲染body阶段，此时开始渲染WriteValue。WriteValue组件也因没有生命周期响应函数，所以直接进入模板。然 后，Start[Span]被执行输出span Tag。接着，Expression又被执行，输出参数value中的值，之后spnd的结束Tag输出。其后，由于WriteValue没有生命周期函 数，所以会直接回到Div的AfterRender阶段，输出div的结束Tag。随后便会调度Form、End[Body]和End[HTML]执行， 过程与前面类似，这里就不鳌述了。</p> 
<p>但是为了提高渲染的效率，Tapestry并没有采用递归的方式调度，而是采用了一种非递归的方式调度页面渲染，并采用了一些消去策略，去除了一些不存在的渲染阶段。比方说，如果一个组件并没有定义BeginRender事件，那么就没有必要激发这个事件。</p> 
<p>为了实现这种非递归的算法，定义了一个叫做org.apache.tapestry5.runtime.RenderCommand的接口，和一些 列实现类，其中有些用于输出文本和HTML标签的，有一些用于输出Template或者Body，有一些用于输出表达式，而组件的每一个阶段都会有一个相 应的实现类。</p> 
<p>PageRenderQueue虽然命名是一个队列，实际上是按照堆栈的方式调度RenderCommand执行。每个RenderCommand 都可以按照需要，在执行的过程中向PageRenderQueue中添加后继的RenderCommand。这样，就可以将递归的算法，转变为非递归的算 法。</p> 
<p>具体算法涉及的面比较广，后续我给出一个这个例子的调用栈的实例，而具体算法就不给出来了。</p> 
<p>这里我只说明一点比较特殊的地方，也就是template和body的渲染。</p> 
<p>前面已经说过，template和body是非常相似的两个部分，不同的呢只是一个是定义在组件的模板中，一个是定义在组件的body中。所以，对它们的处理也是很相似的。</p> 
<p>不论组件是否定义了template，渲染template阶段都是存在的。如果组件定义了模板，则渲染的就是模板中的内容，如果组件没有定义模板，那么渲染的就是body的内容。</p> 
<p>对于body而言，如果组件有body，那么这个阶段就会实际的执行，如果没有，那么就会直接跳过。</p> 
<p>也就是说，对于一个组件，template和body是2选1的，定义了模板，就不会自动的进入body，需要body就不能定义模板。</p> 
<p>那么，如果即希望有模板，又希望有body怎么办呢？</p> 
<p>Tapestry给出的解决方案是在模板中显示的给出一个&lt;t:body/&gt;标签，用来告诉组件，在模板的什么位置插入body的内容。也就是说此时渲染body的行为类似于使用了一个组件&lt;t:body/&gt;完成，而不是框架自动的调度。</p> 
<p>实际上，也只有这样template和body的语言才是明确的，不会因为框架的自动引入调用而打乱的显示内容。</p> 
<p>下面，我们就仔细看看这个例子的实际调用过程，如果感兴趣可以点击展开（点击“+”），查看栈的变化，我已在栈中加入了注释。栈顶的元素是当前正处 理的RenderCommand，下一个栈中新增的元素则是这个RenderCommand向PageRenderQueue中新增的 RenderCommand。这个栈被调度了48次，从00开始，标注在最左边。</p> 
<p>00：Stack[ComponentPageElement[Example]]//Example页面首先入栈处理，在它调用完后，会插入后续的RenderCommand，并准备渲染<br /> 01：Stack[RenderTemplate[Example],//因为没有定义其它的生命周期函数，所以直接进入渲染模板阶段<br /> PostRenderCleanup[Example]]//这个RenderCommand用于检查组件输出的Tag是否匹配，清楚一些渲染时用到的变量<br /> 02：Stack[CompositeRenderCommand[Start[ html], Text[], Start[ body], Text[]], //模板中的内容实际进入站，首先是开始html和body。它们被并在一个command里面处理。而Text则是模板中的换行符<br /> ComponentPageElement[Example:div], //模板中的div组件<br /> Text[], //模板中的换行符<br /> ComponentPageElement[Example:form], //模板中的Form组件<br /> CompositeRenderCommand[Text[], End, Text[], End], //模板中的结束html和body的tag，还有换行符<br /> PostRenderCleanup[Example]]<br /> 03：Stack[ComponentPageElement[Example:div], //div组件进入渲染<br /> Text[], <br /> ComponentPageElement[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 04：Stack[BeginRender[Example:div], //开始div组件的BeginRender阶段，它会插入后续command,，并准备渲染<br /> PostRenderCleanup[Example:div], //清理并检查div组件的渲染过程<br /> Text[], <br /> ComponentPageElement[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 05：Stack[RenderTemplate[Example:div], //渲染div组件的模板入栈，准备渲染<br /> AfterRender[Example:div], //AfterRender入栈，准备执行<br /> PostRenderCleanup[Example:div], <br /> Text[], <br /> ComponentPageElement[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 06：Stack[RenderBody[div], //Div的RenderBody入栈，准备渲染<br /> AfterRender[Example:div], <br /> PostRenderCleanup[Example:div], <br /> Text[], <br /> ComponentPageElement[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 07：Stack[BeforeRenderBody[Example:div], //BeforeRenderBody入栈，准备渲染<br /> AfterRender[Example:div], <br /> PostRenderCleanup[Example:div], <br /> Text[], <br /> ComponentPageElement[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 08：Stack[Block[Body of Example:div, at context:Example.tml, line 3], //body的实际类容入栈，准备渲染<br /> AfterRender[Example:div], <br /> PostRenderCleanup[Example:div], <br /> Text[], <br /> ComponentPageElement[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 09：Stack[Text[], //body中的换行符准备渲染<br /> ComponentPageElement[Example:writevalue], //body中的WriteValue组件<br /> Text[], //body中的换行符<br /> AfterRender[Example:div], <br /> PostRenderCleanup[Example:div], <br /> Text[], <br /> ComponentPageElement[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 10：Stack[ComponentPageElement[Example:writevalue], //WriteValue组件准备渲染<br /> Text[], <br /> AfterRender[Example:div], <br /> PostRenderCleanup[Example:div], <br /> Text[], <br /> ComponentPageElement[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 11：Stack[RenderTemplate[Example:writevalue], //渲染WriteValue组件的模板<br /> PostRenderCleanup[Example:writevalue], //检查并清理WriteValue组件的渲染过程<br /> Text[], <br /> AfterRender[Example:div], <br /> PostRenderCleanup[Example:div], <br /> Text[], <br /> ComponentPageElement[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 12：Stack[CompositeRenderCommand[Start[ span], Text[]], //WriteValue组件模板中的&lt;span&gt;和换行符进入栈，准备渲染<br /> Expansion[PropBinding[expansion Example:writevalue(value)]], //WriteValue组件模板中的表达式<br /> CompositeRenderCommand[Text[], End], //WriteValue组件模板中的换行符和结束tag入栈<br /> PostRenderCleanup[Example:writevalue], <br /> Text[], <br /> AfterRender[Example:div], <br /> PostRenderCleanup[Example:div], <br /> Text[], <br /> ComponentPageElement[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 13：Stack[Expansion[PropBinding[expansion Example:writevalue(value)]], //渲染表达式<br /> CompositeRenderCommand[Text[], End], <br /> PostRenderCleanup[Example:writevalue], <br /> Text[], <br /> AfterRender[Example:div], <br /> PostRenderCleanup[Example:div], <br /> Text[], <br /> ComponentPageElement[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 14：Stack[CompositeRenderCommand[Text[], End], //渲染换行符和结束Tag<br /> PostRenderCleanup[Example:writevalue], <br /> Text[], <br /> AfterRender[Example:div], <br /> PostRenderCleanup[Example:div], <br /> Text[], <br /> ComponentPageElement[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 15：Stack[PostRenderCleanup[Example:writevalue], //检查WriteValue的渲染<br /> Text[], <br /> AfterRender[Example:div], <br /> PostRenderCleanup[Example:div], <br /> Text[], <br /> ComponentPageElement[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 16：Stack[Text[], //渲染换行符<br /> AfterRender[Example:div], <br /> PostRenderCleanup[Example:div], Text[], <br /> ComponentPageElement[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 17：Stack[AfterRender[Example:div], //激发div组件的AfterRender<br /> PostRenderCleanup[Example:div], <br /> Text[], <br /> ComponentPageElement[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 18：Stack[PostRenderCleanup[Example:div], //检查div组件的渲染<br /> Text[], <br /> ComponentPageElement[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 19：Stack[Text[], //渲染换行符<br /> ComponentPageElement[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 20：Stack[ComponentPageElement[Example:form], //渲染那form组件<br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 21：Stack[SetupRender[Example:form], //form组件的SetupRender阶段入栈，准备渲染<br /> PostRenderCleanup[Example:form], //检查form组件command入栈<br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 22：Stack[BeginRender[Example:form], //form组件的BeginRender阶段入栈，准备渲染<br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 23：Stack[RenderTemplate[Example:form], //form组件的templage入栈，准备渲染<br /> AfterRender[Example:form], //form组件的AfterRender阶段入栈<br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 24：Stack[RenderBody[form], //form组件的body入栈，准备渲染<br /> AfterRender[Example:form], <br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 25：Stack[BeforeRenderBody[Example:form], //form组件的BeforeRenderBody阶段激发<br /> AfterRender[Example:form], <br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 26：Stack[Block[Body of Example:form, at context:Example.tml, line 6], //form组件的body实际入栈，准备渲染<br /> AfterRender[Example:form], <br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 27：Stack[Text[], //body中的换行入栈，准备渲染<br /> ComponentPageElement[Example:textfield], //TextField组件入栈<br /> Text[], //换行符入栈<br /> ComponentPageElement[Example:submit], //Submit组件入栈<br /> Text[], //换行符入栈<br /> AfterRender[Example:form], <br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 28：Stack[ComponentPageElement[Example:textfield], //TextField组件入栈，准备渲染<br /> Text[], <br /> ComponentPageElement[Example:submit], <br /> Text[], <br /> AfterRender[Example:form], <br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 29：Stack[SetupRender[Example:textfield], //TextField组件的SetupRender阶段被激发<br /> PostRenderCleanup[Example:textfield], //TextField组件的清理与检查<br /> Text[], <br /> ComponentPageElement[Example:submit], <br /> Text[], <br /> AfterRender[Example:form], <br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 30：Stack[BeginRender[Example:textfield], //TextField组件的BeginRender阶段被激发<br /> PostRenderCleanup[Example:textfield], <br /> Text[], <br /> ComponentPageElement[Example:submit], <br /> Text[], <br /> AfterRender[Example:form], <br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 31：Stack[RenderTemplate[Example:textfield], //TextField组件的RenderTemplate阶段入栈，准备渲染<br /> AfterRender[Example:textfield], //TextField组件的AfterRender入栈<br /> PostRenderCleanup[Example:textfield], <br /> Text[], <br /> ComponentPageElement[Example:submit], <br /> Text[], <br /> AfterRender[Example:form], <br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 32：Stack[RenderBody[textfield], //TextField组件的RenderBody阶段入栈，准备渲染<br /> AfterRender[Example:textfield], //TextField组件的AfterRender<br /> PostRenderCleanup[Example:textfield], <br /> Text[], <br /> ComponentPageElement[Example:submit], <br /> Text[], <br /> AfterRender[Example:form], <br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 33：Stack[AfterRender[Example:textfield], //TextField组件的AfterRender激发<br /> PostRenderCleanup[Example:textfield], <br /> Text[], <br /> ComponentPageElement[Example:submit], <br /> Text[], <br /> AfterRender[Example:form], <br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 34：Stack[PostRenderCleanup[Example:textfield], //检查TextField渲染<br /> Text[], <br /> ComponentPageElement[Example:submit], Text[], <br /> AfterRender[Example:form], <br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 35：Stack[Text[], //渲染换行符<br /> ComponentPageElement[Example:submit], Text[], <br /> AfterRender[Example:form], <br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 36：Stack[ComponentPageElement[Example:submit], //Submit组件准备渲染<br /> Text[], <br /> AfterRender[Example:form], <br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 37：Stack[BeginRender[Example:submit], //Submit组件的BeginRender入栈，准备渲染<br /> PostRenderCleanup[Example:submit], //检查Submit组件渲染command入栈<br /> Text[], <br /> AfterRender[Example:form], <br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 38：Stack[RenderTemplate[Example:submit], //Submit组件的Template入栈，准备渲染那<br /> AfterRender[Example:submit], //Submit组件的AfterRender<br /> PostRenderCleanup[Example:submit], <br /> Text[], <br /> AfterRender[Example:form], <br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 39：Stack[RenderBody[submit], //Submit组件的RenderBody入栈，准备渲染那<br /> AfterRender[Example:submit], <br /> PostRenderCleanup[Example:submit], <br /> Text[], <br /> AfterRender[Example:form], <br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 40：Stack[AfterRender[Example:submit], //Submit组件的AfterRender入栈，准备渲染那<br /> PostRenderCleanup[Example:submit], <br /> Text[], <br /> AfterRender[Example:form], <br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 41：Stack[PostRenderCleanup[Example:submit], //检查submit组件渲染<br /> Text[], <br /> AfterRender[Example:form], <br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 42：Stack[Text[], //渲染换行符<br /> AfterRender[Example:form], <br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 43：Stack[AfterRender[Example:form], //form组件的AfterRender被激发<br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 44：Stack[CleanupRender[Example:form], //form组件的CleanupRender被激发<br /> PostRenderCleanup[Example:form], <br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 45：Stack[PostRenderCleanup[Example:form], //检查Form组件的渲染<br /> CompositeRenderCommand[Text[], End, Text[], End], <br /> PostRenderCleanup[Example]]<br /> 46：Stack[CompositeRenderCommand[Text[], End, Text[], End], //渲染html和body的结束tag<br /> PostRenderCleanup[Example]]<br /> 47：Stack[PostRenderCleanup[Example]]//检查Example的渲染</p> 
<p><strong>4.3中间结构</strong></p> 
<p>下面进入渲染的最后一个部分——中间结构。</p> 
<p>加入这部分并不是应为它有什么特别，而是Tapestry5在这方面做了一次大的调整。页面在渲染过程中，并没有实际的输出到response的输出流中，而是保存在一个中间结构中。</p> 
<p>Tapestry采用了一种类似与XML文档结构的方法组织这个中间结构，也就是说和HTML文档DOM结构很相似的结构。</p> 
<p>采用了这个结构的一个好处是页面在渲染过程中可以随意的在渲染的最后阶段修改和添加某些元素。比如Label组件，关联到了TextField组 件。但是一般Label会在TextField组件之前渲染，所以Label组件得不到TextField组件的ID。但是引入了DOM的中间结构，可以 在TextField组件渲染之后再为Label组件加入TextField的ID。</p> 
<p>前面所谈到的MarkupWriter，正是维护这个DOM结构的输出器，调用它输出的内容会实际的输出到DOM中。</p> 
<p>在页面渲染结束之后，DOM保存的数据就会实际的输出到response的输出流中。</p> 
<p>这时我们在回到前面遗留的一个问题。为什么非组件的HTML标签也被作为DOM的元素处理呢？</p> 
<p>因为这样不仅可以使页面严格的符号XHTML的标准，而且可以更好的处理js和css等资源的引进。也就是说如果页面自己有head标签，就可以在这个标签中添加内容，如果页面没有head那么就可以在合适的位置插入head，再添加所需要的资源。</p> 
<p>本实例输出的HTML代码如下所示，感兴趣可以展开（点击“+”）查看。仔细观察这个结果，会发现Form中有一个input存放了一段经过编码的数据，该数据的作用我会在《<a target="_blank" href="http://blog.csdn.net/mindhawk/archive/2009/12/21/5045815.aspx">解读Tapestry5.1——Form</a>》中详细说明。</p> 
<p>&lt;htm l&gt; <br /> &lt;head&gt;<br /> &lt;link type=&quot;text/css&quot; rel=&quot;stylesheet&quot; href=&quot;/assets/tapestry/5.1.0.5/default.css&quot; mce_href=&quot;assets/tapestry/5.1.0.5/default.css&quot;&gt;&lt;/link&gt;<br /> &lt;link type=&quot;text/css&quot; rel=&quot;stylesheet&quot; href=&quot;/assets/blackbird/5.1.0.5/blackbird.css&quot; mce_href=&quot;assets/blackbird/5.1.0.5/blackbird.css&quot;&gt;&lt;/link&gt;<br /> &lt;meta content=&quot;Apache Tapestry Framework (version 5.1.0.5)&quot; name=&quot;generator&quot;&gt;&lt;/meta&gt;<br /> &lt;mce:script src=&quot;/assets/scriptaculous/5.1.0.5/prototype.js&quot; mce_src=&quot;assets/scriptaculous/5.1.0.5/prototype.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/mce:script&gt;<br /> &lt;mce:script src=&quot;/assets/scriptaculous/5.1.0.5/scriptaculous.js&quot; mce_src=&quot;assets/scriptaculous/5.1.0.5/scriptaculous.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/mce:script&gt;<br /> &lt;mce:script src=&quot;/assets/scriptaculous/5.1.0.5/effects.js&quot; mce_src=&quot;assets/scriptaculous/5.1.0.5/effects.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/mce:script&gt;<br /> &lt;mce:script src=&quot;/assets/tapestry/5.1.0.5/tapestry.js&quot; mce_src=&quot;assets/tapestry/5.1.0.5/tapestry.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/mce:script&gt;<br /> &lt;mce:script src=&quot;/assets/blackbird/5.1.0.5/blackbird.js&quot; mce_src=&quot;assets/blackbird/5.1.0.5/blackbird.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/mce:script&gt;<br /> &lt;mce:script src=&quot;/assets/tapestry/5.1.0.5/tapestry-messages.js&quot; mce_src=&quot;assets/tapestry/5.1.0.5/tapestry-messages.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/mce:script&gt;<br /> &lt;/head&gt;<br /> &lt;body&gt;<br /> &lt;div&gt;<br /> &lt;span&gt;<br /> <br /> &lt;/span&gt;<br /> &lt;/div&gt;<br /> &lt;form onsubmit=&quot;javascript:Tapestry.waitForPage(event);&quot; action=&quot;example.form&quot; method=&quot;post&quot; id=&quot;form&quot; name=&quot;form&quot;&gt;<br /> &lt;div class=&quot;t-invisible&quot;&gt;<br /> &lt;input value=&quot;H4sIAAAAAAAAAFvzloG1XJhB0LUiMbcgJ9WqJLWiJC0zNSeluIjBNL8oXS+xIDE5I1WvJLEgtbikqNJULzm/KDUnM0kvKbE4<br /> Vc8xCSiYmFziBtKiEpxaUlqgGnqY+6Ho8T9MDIw+DNzJ+XklRfk5fom5qSUMQj5ZiWWJ+jmJeen6wSVFmXnp1hUFJQyccEtxucSRVJcEFOUnpxY<br /> XB5cm5WYWF2fm5x1el2KS9m3eOSYGhoqCcgEGPpg1xSAlJUA7HPDakZyfW5Cfl5pXUqwHNrQE04pnD4O+NrsufgHxeGpOai5QOcjjhQx1DIwgj7<br /> JBLAMAHFY4RHQBAAA=&quot; name=&quot;t:formdata&quot; type=&quot;hidden&quot;&gt;&lt;/input&gt;<br /> &lt;/div&gt;<br /> &lt;input id=&quot;textfield&quot; name=&quot;textfield&quot; type=&quot;text&quot;&gt;&lt;/input&gt;&lt;img id=&quot;textfield-icon&quot; class=&quot;t-error-icon t-invisible&quot; alt=&quot;&quot; <br /> src=&quot;/assets/tapestry/5.1.0.5/spacer.gif&quot; mce_src=&quot;assets/tapestry/5.1.0.5/spacer.gif&quot;/&gt;<br /> &lt;input value=&quot;submit&quot; name=&quot;submit&quot; type=&quot;submit&quot;&gt;&lt;/input&gt;<br /> &lt;/form&gt;<br /> &lt;mce:script type=&quot;text/javascript&quot;&gt;&lt;!--<br /> Tapestry.DEBUG_ENABLED = true;<br /> Tapestry.onDOMLoaded(function() {<br /> $('textfield').activate();<br /> });<br /> // --&gt;&lt;/mce:script&gt;&lt;/body&gt;<br /> &lt;/html&gt;</p></div></body></html>